﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace gk.Games.SudokuSolver
{
    public class SudokuPuzzle
    {

        public List<SudokuCellCollection> Rows { get; set; }
        public List<SudokuCellCollection> Columns { get; set; }
        public List<SudokuCellCollection> Squares { get; set; }

        public SudokuPuzzle(string gameData)
        {
            Rows = new List<SudokuCellCollection>(9);
            InitCollection(Rows);
            Columns = new List<SudokuCellCollection>(9);
            InitCollection(Columns);
            Squares = new List<SudokuCellCollection>(9);
            InitCollection(Squares);
            ProcessGameData(gameData);
        }

        private void InitCollection(List<SudokuCellCollection> collection)
        {
            for (int i = 0; i < 9; i++)
            {
                collection.Add(new SudokuCellCollection());
            }
        }

        public bool IsSolved()
        {
            foreach (var row in Rows)
            {
                foreach (var cell in row.Cells)
                {
                    if (!cell.Value.HasValue) return false;
                }
            }
            return true;
        }

        public int NumberOfSolvedCells()
        {
            int solved = 0;
            foreach (var row in Rows)
            {
                foreach (var cell in row.Cells)
                {
                    if (cell.Value.HasValue) solved++;
                }
            }
            return solved;
        }

        public void Solve()
        {
            ProcessCellCollection(Rows);
            ProcessCellCollection(Columns);
            ProcessCellCollection(Squares);
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            foreach (var row in Rows)
            {

                string rowString = "";
                foreach (var cell in row.Cells)
                {
                    if (cell.Value.HasValue)
                        rowString +=cell.Value + ",";
                    else
                        rowString += "_,";
                }
                sb.Append(rowString);//.Substring(0,rowString.Length -1));
                sb.Append(Environment.NewLine);
            }

            return sb.ToString();
        }

        private void ProcessCellCollection(List<SudokuCellCollection> collection)
        {           

            foreach (var col in collection)
            {
                col.ProcessCells();

                foreach (var cell in col.Cells)
                {
                    if (cell.Value.HasValue) continue;
                    if (cell.PossibleValues.Count == 0) throw new ApplicationException("Cell has no value but Possible values is empty");
                    if (cell.PossibleValues.Count == 1)
                    {
                        int val = cell.PossibleValues[0];
                        cell.SetValue(val);
                    }
                }
            }
        }

        private void ProcessGameData(string gameData)
        {
            int row = 0;
            int col = 0;
            foreach (string s in gameData.Split(','))
            {

                SudokuCell cell = new SudokuCell();

                if (s != "_") // parse as its a value.
                {
                    int i = -1;
                    int.TryParse(s, out i);
                    if (i < 0 || i > 9) throw new ApplicationException(string.Format("Invalid Sudoku value {0}", s));
                    cell.SetValue(i);
                }

                if (Rows[row] == null)
                    Rows[row] = new SudokuCellCollection();
                Rows[row].AddCell(cell);
                cell.Validators.Add(Rows[row]);


                if (Rows[col] == null)
                    Rows[col] = new SudokuCellCollection();
                Columns[col].AddCell(cell);
                cell.Validators.Add(Columns[col]);

                int squareRef = GetSquare(row, col);
                if (Squares[squareRef] == null)
                    Squares[squareRef] = new SudokuCellCollection();
                var square = Squares[squareRef];
                square.AddCell(cell);
                cell.Validators.Add(square);


                col++;
                if (col == 9)
                {
                    col = 0;
                    row++;
                }
            }
        }

        private int GetSquare(int row, int col)
        {
            if (row >= 0 && row <= 2)
            {
                if (col >= 0 && col <= 2) return 0;
                if (col >= 3 && col <= 5) return 1;
                if (col >= 6 && col <= 8) return 2;
            }
            else if (row >= 3 && row <= 5)
            {
                if (col >= 0 && col <= 2) return 3;
                if (col >= 3 && col <= 5) return 4;
                if (col >= 6 && col <= 8) return 5;
            }
            else if (row >= 6 && row <= 8)
            {
                if (col >= 0 && col <= 2) return 6;
                if (col >= 3 && col <= 5) return 7;
                if (col >= 6 && col <= 8) return 8;
            }

            throw new ApplicationException(string.Format("Invalid Row or Column reference ({0},{1})", row.ToString(), col.ToString()));
        }


    }
}
